home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / a_utils / expanded.lha / expanded / src / MultiMap.C < prev    next >
C/C++ Source or Header  |  1992-03-19  |  44KB  |  1,638 lines

  1. //
  2. // Linear-Affine-Projective Geometry Package
  3. //
  4. // MultiMap.C
  5. //
  6. // $Header$
  7. //
  8. // William J.R. Longabaugh 
  9. // University of Washington
  10. //
  11. // Some routines are courtesy of Steve Mann.
  12. //
  13. // Implementation of the linear-affine-projective geometry
  14. // package described in William J.R. Longabaugh, "An Expanded
  15. // System for Coordinate-Free Geometric Programming", Master's 
  16. // thesis, University of Washington, 1992.
  17. //
  18. // Copyright (c) 1992, William J.R. Longabaugh
  19. //   Copying, use and development for non-commercial purposes permitted.
  20. //   All rights for commercial use reserved.
  21. //   This software is unsupported and without warranty; it is
  22. //   provided "as is".
  23. //
  24. // ***********************************************************************
  25.  
  26. #include "Lap.h"
  27.  
  28. static int NumSimp(int d, int n);
  29.  
  30. // *********************************************************************
  31. //
  32. // Returns the value ((d + n) choose n).
  33. // C code provided by Steve Mann.
  34. //
  35.  
  36. static int NumSimp(int d, int n)
  37. {
  38.   if ( n == -1 ) {
  39.     return 0;
  40.   } else if ( n == 0 ) {
  41.     return 1;
  42.   } else if ( d == 0 ) {
  43.     return 1;
  44.   } else {
  45.     return NumSimp(d - 1, n) + NumSimp(d, n - 1);
  46.   }
  47. }
  48.  
  49. // *********************************************************************
  50. //
  51. // Multi-indices and tuples of multi-indices are implemented as classes.
  52. // 
  53.  
  54. // Multimaps are currently restricted in degree and dimension.
  55. // These bounds are large enough that they should not create
  56. // difficulties.  The bounds are treated as global constants.
  57.  
  58. const int DIM_MAX = 10;
  59.  
  60. class MultiIndex {
  61.  
  62.   private:
  63.  
  64.         int size;
  65.         int indices[DIM_MAX];
  66.         Boolean antisym;
  67.  
  68.   public:
  69.  
  70.         MultiIndex(void) {};
  71.         MultiIndex(int deg, int dim, Boolean isAnti);
  72.         int SumIndex(void);
  73.         int MItoI(void);
  74.         Boolean NextIndex(void);
  75.     int& Size(void)    {return (size);}
  76.     int& Index(int n)  {return (indices[n]);}
  77. };
  78.  
  79. // Note the use of SYM_MAX, which is defined in Geom.h:
  80.  
  81. class MITuple {
  82.  
  83.   private:
  84.  
  85.         int size;
  86.         MultiIndex entry[SYM_MAX];
  87.         Boolean antisym;
  88.  
  89.   public:
  90.  
  91.     MITuple(MMData& rdat);
  92.     int MITupletoI(void);
  93.     Boolean NextTuple(void);
  94.     int& Size(void)          {return (size);}
  95.     MultiIndex& Entry(int n) {return (entry[n]);}
  96. };
  97.  
  98. // ***********************************************************************
  99. // ***********************************************************************
  100. //
  101. // MultiIndex Class
  102. //
  103. // ***********************************************************************
  104. // ***********************************************************************
  105. //
  106. // Builds an initial index of degree 'deg' and dimension 'dim'.
  107. // Index is tagged as either symmetric or antisymmetric. Code
  108. // for symmetric case is the C++ version of C code provided
  109. // by Steve Mann.
  110. //
  111.  
  112. MultiIndex::MultiIndex(int deg, int dim, Boolean isAnti)
  113. {
  114.   antisym = isAnti;
  115.   size = dim - 1;
  116.  
  117.   if (isAnti) {
  118.     int ones = deg;
  119.     for (int i = size; i >= 0; i--) {
  120.       indices[i] = (ones-- > 0) ? 1 : 0;
  121.     }
  122.   } else {
  123.     indices[size] = deg;
  124.     for (int i = 0; i < size; i++) {
  125.       indices[i] = 0;
  126.     }
  127.   }
  128. }
  129.  
  130. // *********************************************************************
  131. //
  132. // Returns the sum of all the indices in a multi-index.
  133. // This is a C++ version of C code provided by Steve Mann.
  134. //
  135.  
  136. int MultiIndex::SumIndex(void)
  137. {
  138.   int sum = 0;
  139.  
  140.   for (int i = 0; i <= size; i++) {
  141.     sum += indices[i];
  142.   }
  143.   return sum;
  144. }
  145.  
  146. // *********************************************************************
  147. //
  148. // Determines the associated offset into a linear array for a
  149. // multi-index.  Code for symmetric case is the C++ version of
  150. // C code provided by Steve Mann.
  151. //
  152.  
  153. int MultiIndex::MItoI(void)
  154. {
  155.   int retval;
  156.  
  157.   if (antisym) {
  158.     int offset = 0;
  159.     int sum = this->SumIndex();     // degree
  160.     int i = size + 1;               // dimension (remaining slots)
  161.     int readslot = 0;
  162.  
  163.     while (sum > 0) {
  164.       if (indices[readslot++] == 1) {
  165.         if (i > sum) {
  166.           offset += NumSimp((i - 1 - sum), sum);  // (i - 1 choose sum)
  167.         }
  168.         sum--;
  169.       }
  170.       i--;
  171.     }
  172.     retval = offset;
  173.  
  174.   } else {
  175.     int sum = this->SumIndex();
  176.     int value = 0;
  177.  
  178.     for (int i = size; i > 0; i--) {
  179.       sum -= indices[i];
  180.       value += NumSimp(i, sum - 1);
  181.     }
  182.     retval = value;
  183.   }
  184.  
  185.   return (retval);
  186. }
  187.  
  188. // *********************************************************************
  189. //
  190. // Transform this multi-index into the next index.  Return TRUE if this
  191. // was done successfully; return FALSE if index is a maximum index.
  192. // Symmetric case code is C++ version of C code provided by Steve Mann.
  193. // No guarantees are made about the state of the index if FALSE is
  194. // returned.
  195. //
  196.  
  197. Boolean MultiIndex::NextIndex(void)
  198. {
  199.   if (antisym) {
  200.     Boolean can_shift = FALSE;
  201.     int num_shiftable = 0;
  202.  
  203. //  Start at the most significant slot and cruise down, looking
  204. //  for a 1 that can be shifted up.  Count the number of 1s we
  205. //  pass and change these 1s to zeroes.  When the shift is enabled
  206. //  and a zero is found, do it and then change the appropriate
  207. //  number of zeros back to 1's:
  208.  
  209.     for (int i = size; i >= 0; i--) {
  210.       if (indices[i] == 1) {
  211.         can_shift = TRUE;
  212.         num_shiftable++;
  213.         indices[i] = 0;
  214.       } else if (can_shift) {
  215.         indices[i] = 1;
  216.         num_shiftable--;
  217.         for (int j = size; num_shiftable > 0; j--) {
  218.           indices[j] = 1;
  219.           num_shiftable--;
  220.         }
  221.         return (TRUE);
  222.       }
  223.     }
  224.     return (FALSE);
  225.  
  226.   } else {
  227.     int i;
  228.  
  229.     for (i = size; i >= 0; i--) {
  230.       if (indices[i] != 0) {
  231.         break;
  232.       }
  233.     }
  234.     if (i <= 0) {
  235.       return (FALSE);
  236.     }
  237.     if (i == size) {
  238.       indices[size]--;
  239.       indices[size - 1]++;
  240.     } else {
  241.       indices[size] = indices[i] - 1;
  242.       indices[i] = 0;
  243.       indices[i - 1]++;
  244.     }
  245.     return (TRUE);
  246.   }
  247. }
  248.  
  249. // ***********************************************************************
  250. // ***********************************************************************
  251. //
  252. // MITuple Class
  253. //
  254. // ***********************************************************************
  255. // *********************************************************************
  256. //
  257. // Creates an initial tuple for given dimension/degree characteristics
  258. // encoded in the MMData object.
  259. //
  260.  
  261. MITuple::MITuple(MMData& rdat)
  262. {
  263.   antisym = rdat.IsAntisym();
  264.   size = rdat.Numsym();
  265.   for (int i = 0; i < size; i++) {
  266.     entry[i] = MultiIndex(rdat.Deg(i), rdat.Dim(i), antisym);
  267.   }
  268. }
  269.  
  270. // *********************************************************************
  271. //
  272. // Determine the associated offset into linear storage for this
  273. // multi-index tuple.  The first tuple has index 0.
  274. //
  275.  
  276. int MITuple::MITupletoI(void)
  277. {
  278.   int value = 0;
  279.  
  280. // With antisymmetric maps, the implementation is restricted to
  281. // a single symmetry set (i.e. one multi-index):
  282.  
  283.   if (antisym) {
  284.     value = entry[0].MItoI();
  285.   } else {
  286.  
  287. //  Start with the rightmost entry in the tuple, and figure out
  288. //  its offset.  Multiply this by the block size.  Do this for
  289. //  each tuple entry, and sum the results.
  290.  
  291.     int blocksize = 1;
  292.     for (int i = size - 1; i >= 0; i--) {
  293.       value += (entry[i].MItoI() * blocksize);
  294.       blocksize *= NumSimp(entry[i].Size(), entry[i].SumIndex());
  295.     }
  296.   }
  297.   return value;
  298. }
  299.  
  300. // *********************************************************************
  301. //
  302. //  Given a tuple, make it into the next tuple.  Return TRUE if this
  303. //  was done successfully; return FALSE if tuple is max tuple.
  304. //
  305.  
  306. Boolean MITuple::NextTuple(void)
  307. {
  308. // Call NextIndex on the rightmost tuple entry.  If we get TRUE,
  309. // we are happy.  If we get FALSE, set the overflowing entry to the
  310. // first index and increment the next entry.
  311.  
  312.   for (int i = size - 1; i >= 0; i--) {
  313.     if (entry[i].NextIndex()) {
  314.       return TRUE;
  315.     } else {
  316.       int sum = entry[i].SumIndex();
  317.       int misize = entry[i].Size();
  318.       entry[i] = MultiIndex(sum, misize + 1, antisym);
  319.     }
  320.   }
  321.   return FALSE;
  322. }
  323.  
  324.  
  325. // ***********************************************************************
  326. // ***********************************************************************
  327. //
  328. // MMData Class
  329. //
  330. // ***********************************************************************
  331. // ***********************************************************************
  332. //
  333. // Need to do memberwise initialization, since Matrix class members need
  334. // to do memberwise initialization:
  335. //
  336.  
  337. MMData::MMData(MMData& v) : t(v.t)
  338. {
  339.   numsym = v.numsym;
  340.   isAntisym = v.isAntisym;
  341.   for (int k = 0; k < numsym; k++) {
  342.     deg[k] = v.deg[k];
  343.     dim[k] = v.dim[k];
  344.   }
  345. }
  346.  
  347. // ***********************************************************************
  348. //
  349. // Need to do memberwise assignment, since Matrix class members need
  350. // to do memberwise assignment:
  351. //
  352.  
  353. MMData& MMData::operator=(MMData& v)
  354. {
  355.   t = v.t;
  356.   numsym = v.numsym;
  357.   isAntisym = v.isAntisym;
  358.   for (int k = 0; k < numsym; k++) {
  359.     deg[k] = v.deg[k];
  360.     dim[k] = v.dim[k];
  361.   }
  362.   return *this;
  363. }
  364.  
  365. // ***********************************************************************
  366. //
  367. // Output for debugging
  368. //
  369.  
  370. void MMData::debug_out(ostream &c, int indent) 
  371. {
  372.   char *ibloc = new char[indent + 1];
  373.   for (int i = 0; i < indent; i++) {
  374.     *(ibloc + i) = ' ';
  375.   }
  376.   *(ibloc + indent) = '\0';
  377.  
  378.   c << ibloc << ast;
  379.   c << ibloc << "MMData object\n";
  380.   if (isAntisym) {
  381.     c << ibloc << "Map is antisymmetric\n";
  382.   } else {
  383.     c << ibloc << "Map is partially (or fully) symmetric\n";
  384.   }
  385.  
  386.   c << ibloc << "Matrix representation of control net:\n";
  387.   t.debug_out(c, indent + ERR_IND);
  388.   c << ibloc << "Number of symmetry sets: " << numsym << "\n";
  389.  
  390.   c << ibloc << "Degree of each symmetry set:";
  391.   for (i = 0; i < numsym; i++) {
  392.     if (i % 4 == 0) {c << "\n" << ibloc;}
  393.     c << deg[i] << "  ";
  394.   }
  395.   c << "\n";
  396.  
  397.   c << ibloc << "Dimension of each symmetry set:";
  398.   for (i = 0; i < numsym; i++) {
  399.     if (i % 4 == 0) {c << "\n" << ibloc;}
  400.     c << dim[i] << "  ";
  401.   }
  402.   c << "\n";
  403.   c << ibloc << ast;
  404.  
  405.   delete ibloc;
  406.   return;
  407. }
  408.  
  409. // ***********************************************************************
  410. //
  411. // Returns the size of the net needed to store the map:
  412. //
  413.  
  414. int MMData::NetSize(void)
  415. {
  416.   int total;
  417.  
  418. // Antisym: (dim choose deg)
  419.  
  420.   if (isAntisym) {
  421.     total = NumSimp(dim[0] - deg[0], deg[0]);
  422.   } else {
  423.  
  424. //  For each symmetry set, determine the number of entries. 
  425. //  (dim + deg - 1 choose deg)  The total number of slots is
  426. //  the product of these entries:
  427.  
  428.     total = 1;
  429.     for (int i = 0; i < numsym; i++) {
  430.       total *= NumSimp(dim[i] - 1, deg[i]);
  431.     }
  432.   }
  433.   return total;
  434. }
  435.  
  436. // ***********************************************************************
  437. //
  438. // Returns the number of the symmetry set corresponding to a particular
  439. // argument nember:
  440. //
  441.  
  442. int MMData::WhichSet(int argnum)
  443. {
  444.   int inset;
  445.   int symsum = deg[0];
  446.   for (inset = 0; inset < numsym; inset++) {
  447.     if (argnum < symsum) {
  448.       break;
  449.     }
  450.     symsum += deg[inset + 1];
  451.   } 
  452.   return (inset);
  453. }
  454.  
  455. // ***********************************************************************
  456. //
  457. // Updates the MMData to remove the specified symmetry set if it has been
  458. // fully evaluated:
  459. //
  460.  
  461. void MMData::RemoveSymSet(int inset)
  462. {
  463.   if (deg[inset] == 0) {
  464.     numsym--;
  465.     for (int i = inset; i < numsym; i++) {
  466.       deg[i] = deg[i + 1];
  467.       dim[i] = dim[i + 1];
  468.     }
  469.   }
  470. }
  471.  
  472. // ***********************************************************************
  473. // ***********************************************************************
  474. //
  475. // MultiMap Class
  476. //
  477. // ***********************************************************************
  478. // ***********************************************************************
  479. //
  480. // Private/protected member functions
  481. //
  482. // ***********************************************************************
  483. //
  484. // Given a multi-index tuple, build an argument of standard basis elements
  485. // for the tuple:
  486. //
  487.  
  488. void MultiMap::BasisArg(MITuple& tup, Space& s, GeObList* gl)
  489. {
  490. // Step thru each symmetry set.  The value of each field of the
  491. // MultiIndex in that set tells how many times to include each 
  492. // standard basis member:
  493.  
  494.   int slot = 0;
  495.   *gl = GeObList();
  496.   for (int k = 0; k < tup.Size(); k++) {
  497.     Basis curbas = s[slot].StdBasis();
  498.     for (int j = tup.Entry(k).Size(); j >= 0; j--) {
  499.       int n = tup.Entry(k).Index(j);
  500.       for (int m = 0; m < n; m++) {
  501.         *gl = *gl + GeObList(curbas[tup.Entry(k).Size() - j]);
  502.         slot++;
  503.       }
  504.     }
  505.   }
  506.   return;
  507. }
  508.  
  509. // ***********************************************************************
  510. //
  511. // Get the matrix representation of the nth standard basis object
  512. // (applies to multimaps that are in fact simple maps):
  513. //
  514.  
  515. Matrix MultiMap::GetStdImage(int n)
  516. {
  517.   static char* sig = "Matrix MultiMap::GetStdImage(int)";
  518.  
  519. // Make sure the map is in fact a simple map:
  520.  
  521.   if ((data.Numsym() != 1) || (data.Deg(0) != 1)) {
  522.     errh.ErrorExit(sig, "Multimap not a simple map", *this);
  523.   }
  524.  
  525. // The matrix of the map encodes the images of the objects in
  526. // the standard basis.  Get the nth image.
  527.  
  528.   MITuple tup(data);
  529.   for (int k = 0; k < n; k++) {
  530.     tup.NextTuple();
  531.   }
  532.  
  533.   Matrix retval = data.Net()[tup.MITupletoI()];
  534.   return (retval);
  535. }
  536.  
  537. // ***********************************************************************
  538. //
  539. // Does a single layer of multimap evaluation for both symmetric and
  540. // antisymmetric cases:
  541. //
  542.  
  543. void MultiMap::Eval(int argnum, ScalarList& coords, MMData* rdat)
  544. {
  545.   if (rdat->IsAntisym()) {
  546.  
  547.     // Calculate the sign to apply to this evaluation.  Depends on
  548.     // whether we are evaluating an odd or even argument:
  549.  
  550.     Scalar oesign =  ((argnum % 2) == 0) ? 1.0 : -1.0;
  551.  
  552.     // Antisymmetric maps consist of only 1 symmetry set.
  553.     // Generate a first tuple that has the degree of this
  554.     // symmetry set reduced.  Figure out how many new vectors
  555.     // will be generated in this layer of evaluation and build
  556.     // a new matrix.  Evaluate and copy the new matrix back.
  557.     
  558.     rdat->Deg(0)--;
  559.     Matrix newmat(rdat->NetSize(), rdat->Net().Columns()); 
  560.     MITuple tup(*rdat);
  561.     Boolean working = TRUE;
  562.     while (working) {
  563.       this->PtEvalAnti(coords, &tup, rdat, &newmat, oesign);
  564.       working = tup.NextTuple();
  565.     }
  566.     rdat->Net() = newmat;
  567.  
  568.   } else {  // Symmetric maps
  569.  
  570.     // Figure out which symmetry set the argument belongs to:
  571.  
  572.     int inset = rdat->WhichSet(argnum);
  573.  
  574.     // The net will be collapsed within that symmetry set. 
  575.     // Generate a first tuple that has the degree of the appropriate
  576.     // symmetry set reduced and evaluate:
  577.     
  578.     rdat->Deg(inset)--;
  579.  
  580.     MITuple tup(*rdat);
  581.     Boolean working = TRUE;
  582.     while (working) {
  583.       this->PtEval(coords, &tup, inset, rdat);
  584.       working = tup.NextTuple();
  585.     }
  586.  
  587.    // Possibly update the symmetry set information: 
  588.  
  589.     rdat->RemoveSymSet(inset);
  590.   }
  591. }
  592.  
  593. // ***********************************************************************
  594. //
  595. // Evaluates a new net entry for symmetric maps:
  596. //
  597.  
  598. void MultiMap::PtEval(ScalarList& coords, MITuple* tup, 
  599.               int el, MMData* rdat)
  600. {
  601.   Matrix hold = ZeroMatrix(1, rdat->Net().Columns());
  602.   int n = tup->Entry(el).Size();
  603.   int cslot = n;
  604.  
  605.   for (int i = 0; i <= n; i++) {
  606.     tup->Entry(el).Index(i)++;
  607.     hold += coords[cslot--] * rdat->Net()[tup->MITupletoI()];
  608.     tup->Entry(el).Index(i)--;
  609.   }
  610.   rdat->Net()[tup->MITupletoI()] = hold[0];
  611.  
  612. // ***********************************************************************
  613. //
  614. // Evaluates a new net entry for antisymmetric maps.  Note that results
  615. // are stored in another matrix (antisymmetric evaluation cannot be
  616. // done in place):
  617. //
  618.  
  619. void MultiMap::PtEvalAnti(ScalarList& coords, MITuple* tup, 
  620.                   MMData* rdat, Matrix* newmat, Scalar oesign)
  621. {
  622.   Scalar altsign;
  623.   int onecount = 0;
  624.   Matrix hold = ZeroMatrix(1, rdat->Net().Columns());
  625.   int n = tup->Entry(0).Size();
  626.  
  627.   for (int i = n; i >= 0; i--) {
  628.     if (tup->Entry(0).Index(i) == 0) {
  629.       tup->Entry(0).Index(i)++;
  630.       altsign = ((onecount % 2) == 0) ? 1.0 : -1.0;
  631.       hold += oesign * altsign *
  632.           coords[n - i] * rdat->Net()[tup->MITupletoI()];
  633.       tup->Entry(0).Index(i)--;
  634.     } else {
  635.       onecount++;
  636.     }
  637.   }
  638.   (*newmat)[tup->MITupletoI()] = hold[0];
  639. }
  640.  
  641. // ***********************************************************************
  642. //
  643. // Partial evaluation core routine.
  644. //
  645.  
  646. void MultiMap::PartialEval(GeObList& t, SpaceList* nd, MultiMap* ret)
  647. {
  648.   static char* sig = 
  649.       "void MultiMap::PartialEval(GeObList&, SpaceList*, MultiMap*)";
  650.  
  651. // We have no business trying to evaluate maps that are fully evaluated:
  652.  
  653.   if (fulleval) {
  654.     errh.ErrorExit(sig, "Map is already fully evaluated", *this, t);
  655.   }
  656.  
  657. // Transfer the map data into a holding block:
  658.  
  659.   MMData partial = data;
  660.  
  661. // Require that the length of the object list match the component count of
  662. // the domain space:
  663.  
  664.   int numobj = t.Length();
  665.   if (numobj != domain.CPSpaceSize()) {
  666.     errh.ErrorExit(sig,
  667.           "Number of objects does not match cartesian product space size",
  668.           *this, t);
  669.   }
  670.  
  671. // If at any time we get a tangent space vector in an argument slot
  672. // that expects an affine point, the range of the map, if it is an
  673. // affine space, changes to a tangent space! 
  674.  
  675.   Boolean SwitchRange = FALSE;
  676.  
  677. // Run through the list of geometric objects, doing one layer of
  678. // evaluation for each one.
  679.  
  680.   ScalarList coords;
  681.   int evalarg = 0;
  682.   GeOb temp;
  683.   Space tempsp;
  684.  
  685.   for (int j = 0; j < numobj; j++) {
  686.  
  687.     // Skip evaluation if the object is NULL.  We need to record the
  688.     // space that was skipped so we know what the new domain space
  689.     // is when we are done:
  690.  
  691.     if (t[j].Holds() == NULL_GEOB) {
  692.       *nd = *nd + SpaceList(domain[j]);
  693.       evalarg++;
  694.       continue;
  695.     } 
  696.     
  697.     // Cast into appropriate space.  Tangent space vectors can be used instead
  698.     // of affine points:
  699.  
  700.     GeObType targtype = domain[j].NativeType();
  701.     temp = t[j];
  702.     if (t[j].CanMapTo(targtype)) {
  703.       temp = temp.MapTo(targtype);
  704.       tempsp = temp.SpaceOf();
  705.     } else if ((targtype == AFF_POINT) && temp.CanMapTo(AFF_VECTOR)) {
  706.       temp = temp.MapTo(AFF_VECTOR);
  707.       tempsp = temp.SpaceOf().GetSpace(AFFINE);
  708.       SwitchRange = (rettype == AFF_POINT);
  709.     } else {
  710.       errh.ErrorExit(sig, "Object cannot be mapped to domain space", *this, t);
  711.     }
  712.  
  713.     if (tempsp != domain[j]) {
  714.       errh.ErrorExit(sig, "Object cannot be mapped to domain space", *this, t);
  715.     } 
  716.  
  717.     // Determine the coordinates of the argument wrt the standard
  718.     // basis of the component space, and pass this off to the single
  719.     // layer evaluation routine:
  720.  
  721.     coords = (tempsp.StdBasis())(temp);
  722.     this->Eval(evalarg, coords, &partial);
  723.   }
  724.  
  725. // After this loop, "partial" holds the new data for the multimap,
  726. // but its net matrix is padded with useless data left over
  727. // from the evaluation.  Copy the data to the new map's MMData, only
  728. // keeping the useful stuff.  If the range has switched, the representation
  729. // of the net needs to be truncated:
  730.  
  731.   Matrix dehoist;
  732.   if (SwitchRange) dehoist = Transpose(range.HoistTangent());
  733.   ret->range = (SwitchRange) ? range.GetSpace(TANGENT) : range;
  734.   ret->data = partial;
  735.   int rowcount = partial.NetSize();
  736.   int colcount = ret->range.MatrixSize();
  737.   ret->data.Net() = Matrix(rowcount, colcount);
  738.   for (int r = 0; r < rowcount; r++) {
  739.     if (SwitchRange) {
  740.       ret->data.Net()[r] = (partial.Net()[r] * dehoist)[0];
  741.     } else {
  742.       ret->data.Net()[r] = partial.Net()[r];
  743.     }
  744.   }  
  745.  
  746. // Figure out odds and ends that are needed to build the new multimap:
  747.  
  748.   ret->holds = holds;
  749.   ret->fulleval = (evalarg == 0);
  750.   ret->rettype = ret->range.NativeType();
  751.   return;
  752. }
  753.  
  754. // ***********************************************************************
  755. //
  756. // Dumps the data for a multimap:
  757. //
  758.  
  759. void MultiMap::data_out(ostream &c, int indent, char* name) 
  760. {
  761.   char *ibloc = new char[indent + 1];
  762.   for (int i = 0; i < indent; i++) {
  763.     *(ibloc + i) = ' ';
  764.   }
  765.   *(ibloc + indent) = '\0';
  766.  
  767.   c << ibloc << ast;
  768.   c << ibloc << name;
  769.   c << ibloc << "Type of multimap: ";
  770.   MultiTypeOut(c, type);
  771.   c << "\n";
  772.   c << ibloc << "Currently holds: ";
  773.   MultiTypeOut(c, holds);
  774.   c << "\n";
  775.   if (holds != NULL_MULTI) {
  776.     c << ibloc << "Domain space:\n";
  777.     domain.debug_out(c, indent + ERR_IND);
  778.     c << ibloc << "Range space:\n";
  779.     range.debug_out(c, indent + ERR_IND);
  780.     c << ibloc << "Domain type: ";
  781.     GeObTypeOut(c, domtype);
  782.     c << "\n";
  783.     c << ibloc << "Return type: ";
  784.     GeObTypeOut(c, rettype);
  785.     c << "\n";
  786.     if (fulleval) {
  787.       c << ibloc << "Map has been fully evaluated\n";
  788.     } else {
  789.       c << ibloc << "Map has not been fully evaluated\n";
  790.     }
  791.     data.debug_out(c, indent + ERR_IND);
  792.   }
  793.   c << ibloc << ast;
  794.  
  795.   delete ibloc;
  796.   return;
  797. }
  798.  
  799. // ***********************************************************************
  800. //
  801. // Public member functions
  802. //
  803. // ***********************************************************************
  804. //
  805. // Need to do memberwise initialization, since class members need
  806. // to do memberwise initialization:
  807. //
  808.  
  809. MultiMap::MultiMap(MultiMap& v) : range(v.range),
  810.                   domain(v.domain), data(v.data)
  811. {
  812.   holds = v.holds;
  813.   type = ANY_MULTI;
  814.   fulleval = v.fulleval;
  815.   domtype = v.domtype;
  816.   rettype = v.rettype;
  817. }
  818.  
  819. // ***********************************************************************
  820. //
  821. // Need to do memberwise assignment, since class members need
  822. // to do memberwise assignment:
  823. //
  824.  
  825. MultiMap& MultiMap::operator=(MultiMap& v)
  826. {
  827. //
  828. // This weird checking is required to bypass the apparent inheritance of
  829. // assignment under g++ 1.37:
  830. //
  831.   if ((type != ANY_MULTI) && (type != v.holds) && (v.holds != NULL_MULTI)) {
  832.     errh.ErrorExit("MultiMap& MultiMap::operator=(MultiMap&)",
  833.                    "Illegal assignment attempted", *this, v);
  834.   }
  835.   range = v.range;
  836.   domain = v.domain;
  837.   data = v.data;
  838.   holds = v.holds;
  839.   fulleval = v.fulleval;
  840.   domtype = v.domtype;
  841.   rettype = v.rettype;
  842.   return *this;
  843. }
  844.  
  845. // ***********************************************************************
  846. //
  847. // Output for debugging:
  848. //
  849.  
  850. void MultiMap::debug_out(ostream &c, int indent) 
  851. {
  852.   static char* name = "MultiMap Object\n";
  853.   this->data_out(c, indent, name);
  854.   return;
  855. }
  856.  
  857. // ***********************************************************************
  858. //
  859. // Converts a linear or affine map into a multimap:
  860. //
  861.  
  862. MultiMap::MultiMap(Map &s)
  863. {
  864.   static char* sig = "MultiMap::MultiMap(Map&)";
  865.  
  866.   type = ANY_MULTI;
  867.  
  868. // The map must be linear or an affine map from an entire affine space:
  869.  
  870.   if ((s.Holds() != LIN_MAP) && (s.Holds() != AFF_MAP)) {
  871.     errh.ErrorExit(sig, "Map must be linear or affine", s);
  872.   }
  873.  
  874.   if ((s.Holds() == AFF_MAP) &&
  875.       (s.Domain().EmbeddingSpace().Holds() != AFF_SPACE)) {
  876.     errh.ErrorExit(sig, "Domain of affine map must be an affine space", s);
  877.   }
  878.  
  879. // The map must be from a full space:
  880.  
  881.   if (!s.Domain().IsFullSpace()) {
  882.     errh.ErrorExit(sig, "Domain must be an entire space", s);
  883.   }
  884.  
  885.   range = s.Range().EmbeddingSpace();
  886.   domain = s.Domain().EmbeddingSpace();
  887.  
  888.   if ((range.Holds() != VEC_SPACE) && (range.Holds() != AFF_SPACE)) {
  889.     errh.ErrorExit(sig, "Range must be an affine or vector space", range);
  890.   }
  891.  
  892. // The domain must be an atomic space:
  893.  
  894.   if (domain.CPSpaceSize() != 1) {
  895.     errh.ErrorExit(sig, "Domain must be an atomic space", s);
  896.   }
  897.  
  898.   holds = (s.Holds() == AFF_MAP) ? MULTI_AFFINE : MULTI_LINEAR;
  899.   fulleval = (domain.Holds() == NULL_SPACE); 
  900.   domtype = s.DomainType();
  901.   rettype = s.ReturnType();
  902.   data.IsAntisym() = FALSE;
  903.  
  904. // These maps have only one symmetry set:
  905.  
  906.   data.Numsym() = 1;
  907.   data.Deg(0) = 1;
  908.   data.Dim(0) = domain.MatrixSize();
  909.  
  910.   if (data.Dim(0) > DIM_MAX) {
  911.     errh.ErrorExit(sig, "Multimap dimension restriction exceeded", s);
  912.   }
  913.  
  914. // The matrix of the map encodes the images of the objects in
  915. // the standard basis.
  916.  
  917.   Matrix temp = s.MatrixRep();
  918.   MITuple tup(data);
  919.   data.Net() = Matrix(data.Dim(0), temp.Columns());
  920.  
  921.   for (int k = 0; k < data.Dim(0); k++) {
  922.     data.Net()[tup.MITupletoI()] = temp[k];
  923.     tup.NextTuple();
  924.   }
  925. }
  926.  
  927. // ***********************************************************************
  928. //
  929. // Full evaluation.
  930. //
  931.  
  932. GeOb MultiMap::operator()(GeOb& v)
  933. {
  934.   static char* sig = "GeOb MultiMap::operator()(GeOb&)";
  935.  
  936. // We have no business trying to evaluate maps that are fully evaluated:
  937.  
  938.   if (fulleval) {
  939.     errh.ErrorExit(sig, "Map is already fully evaluated", *this, v);
  940.   }
  941.  
  942. // Cast the argument into the domain space.  If the argument is a 
  943. // vector tuple and we expect a point tuple, can still go:
  944.  
  945.   GeOb temp;
  946.   Space tempsp;
  947.   GeObType ret;
  948.   Space rng;
  949.   Boolean dehoist = FALSE;
  950.  
  951.   if (v.CanMapTo(domtype)) {
  952.     temp = v.MapTo(domtype);
  953.     tempsp = temp.SpaceOf();
  954.     ret = rettype;
  955.     rng = range;
  956.   } else if ((domtype == AFF_POINT) && v.CanMapTo(AFF_VECTOR)) {
  957.     temp = v.MapTo(AFF_VECTOR);
  958.     tempsp = temp.SpaceOf().GetSpace(AFFINE);
  959.     if (rettype == AFF_POINT) {
  960.       ret = AFF_VECTOR;
  961.       rng = range.GetSpace(TANGENT);
  962.       dehoist = TRUE;
  963.     } else {
  964.       ret = rettype; 
  965.       rng = range;
  966.     }
  967.   } else {
  968.     errh.ErrorExit(sig, "Object cannot be mapped to domain space", *this, v);
  969.   }
  970.  
  971.   if (tempsp != domain) {
  972.     errh.ErrorExit(sig, "Object cannot be mapped to domain space", *this, v);
  973.   } 
  974.  
  975. // Step through each component of the argument.  For each component,
  976. // determine the coordinates the the argument wrt the standard
  977. // basis of the component space, and pass this off to the single
  978. // layer evaluation routine:
  979.  
  980.   int n = tempsp.CPSpaceSize();
  981.   MMData partial = data;
  982.  
  983.   for (int j = 0; j < n; j++) {
  984.     ScalarList coords = (tempsp[j].StdBasis())(temp[j]);
  985.     this->Eval(0, coords, &partial);
  986.   }
  987.  
  988. // After this loop, "partial" holds the std. coords of the object
  989. // in the range space.  Build the GeOb from this information.
  990. // We may need to drop the representation down to the tangent space:
  991.  
  992.   Matrix retmat(partial.Net()[0]);
  993.   if (dehoist) {
  994.     retmat *= Transpose(range.HoistTangent());
  995.   }
  996.   GeOb retval(ret, rng, retmat);
  997.   return retval;
  998. }
  999.  
  1000. // ***********************************************************************
  1001. //
  1002. // Partial evaluation, where we need to build a new CP Space for the
  1003. // new domain:
  1004. //
  1005.  
  1006. MultiMap MultiMap::operator()(GeObList &t)
  1007. {
  1008.   static char* sig = "MultiMap MultiMap::operator()(GeObList&)";
  1009.   MultiMap retval;
  1010.   SpaceList ndlist;
  1011.  
  1012. // Call the core routine to do the grunt work:
  1013.  
  1014.   this->PartialEval(t, &ndlist, &retval);
  1015.  
  1016. // Build the required domain space:
  1017.  
  1018.   if (retval.fulleval) {
  1019.     retval.domain = Space();
  1020.   } else if (ndlist.Length() == 1) {
  1021.     retval.domain = ndlist[0];
  1022.   } else if (holds == MULTI_LINEAR) {
  1023.     retval.domain = VSpace("Domain for partially evaluated MLM",
  1024.                ndlist, FALSE);
  1025.   } else {
  1026.     retval.domain = ASpace("Domain for partially evaluated MAM",
  1027.                ndlist, FALSE);
  1028.   }
  1029.   retval.domtype = retval.domain.NativeType();
  1030.  
  1031.   return retval;
  1032. }
  1033.  
  1034. // ***********************************************************************
  1035. //
  1036. // Partial evaluation with specified target domain space:
  1037. //
  1038.  
  1039. MultiMap MultiMap::operator()(Space& newdom, GeObList &t)
  1040. {
  1041.   static char* sig = "MultiMap MultiMap::operator()(Space&, GeObList&)";
  1042.   MultiMap retval;
  1043.   SpaceList ndlist;
  1044.  
  1045. // Call the core routine to do the grunt work:
  1046.  
  1047.   this->PartialEval(t, &ndlist, &retval);
  1048.  
  1049. // Make sure the new domain space provided matches the one needed:
  1050.  
  1051.   int num = ndlist.Length(); 
  1052.   if (num != newdom.CPSpaceSize()) {
  1053.       errh.ErrorExit(sig,
  1054.              "Target domain space size mismatch", *this, newdom, t);
  1055.     }
  1056.   for (int j = 0; j < num; j++) {
  1057.     if (ndlist[j] != newdom[j]) {
  1058.       errh.ErrorExit(sig, "Target domain space mismatch", *this, newdom, t);
  1059.     }
  1060.   } 
  1061.  
  1062. // Finish building the new map:
  1063.  
  1064.   retval.domain = newdom;
  1065.   retval.domtype = newdom.NativeType();
  1066.  
  1067.   return retval;
  1068. }
  1069.  
  1070. // ***********************************************************************
  1071. // ***********************************************************************
  1072. //
  1073. // MLM Class
  1074. //
  1075. // ***********************************************************************
  1076. // ***********************************************************************
  1077. //
  1078. // Private/protected member functions
  1079. //
  1080. // ***********************************************************************
  1081. //
  1082. // Creates the standard multi-linear maps for Euclidean spaces.
  1083. //
  1084.  
  1085. MLM::MLM(StdMaps mpt, VSpace& sp)
  1086. {
  1087.   type = MULTI_LINEAR;
  1088.   switch (mpt) {
  1089.     case INNER_PROD:
  1090.       this->DotProduct(sp);
  1091.       break;
  1092.     case CROSS_PROD:
  1093.       this->CrossProduct(sp);
  1094.       break;
  1095.     default:
  1096.       errh.ErrorExit("MLM::MLM(StdMaps, VSpace&)", "Unexpected error");
  1097.       break;
  1098.   }
  1099. }
  1100.  
  1101. // ***********************************************************************
  1102.  
  1103. void MLM::DotProduct(VSpace& sp)
  1104. {
  1105.   static char* sig = "void MLM::DotProduct(VSpace&)";
  1106.  
  1107. // Build data info.  There is currently a size restriction on MultiMaps: 
  1108.  
  1109.   if (sp.Dim() > DIM_MAX) {
  1110.     errh.ErrorExit(sig, "Multimap dimension restriction exceeded", sp);
  1111.   }
  1112.   data.Numsym() = 1;
  1113.   data.IsAntisym() = FALSE;
  1114.   data.Deg(0) = 2;
  1115.   data.Dim(0) = sp.Dim();
  1116.  
  1117. // Fill in the data matrix.  Entries are either 0 or 1 (vectors in the
  1118. // space Reals).  The slot in the data matrix depends on the multi-index
  1119. // ordering:
  1120.  
  1121.   MITuple tup(data);
  1122.   int count = data.NetSize();
  1123.   data.Net() = Matrix(count, 1);
  1124.  
  1125.   for (int k = 0; k < count; k++) {
  1126.     data.Net()[tup.MITupletoI()] = this->DotResult(tup);
  1127.     tup.NextTuple();
  1128.   }
  1129.  
  1130.   holds = MULTI_LINEAR;
  1131.   fulleval = FALSE;  
  1132.   domain = VSpace("Argument space for dot product", SpaceList(sp, sp), FALSE);
  1133.   range = Reals;
  1134.   domtype = domain.NativeType();
  1135.   rettype = Reals.NativeType();
  1136. }
  1137.  
  1138. // ***********************************************************************
  1139. //
  1140. // Returns the image corresponding to the given tuple for the standard
  1141. // Euclidean dot product.
  1142. //
  1143.  
  1144. Scalar MLM::DotResult(MITuple& tup)
  1145. {
  1146. // The tuple is only going to have one symmetry group.  The maximum
  1147. // degree is 2.  Scan the MultiIndex.  If we hit a 2, the arguments
  1148. // match and we need a 1.0.  Otherwise, the result is 0.0:
  1149.  
  1150.   int i;
  1151.   int top = tup.Entry(0).Size();
  1152.  
  1153.   for (i = 0; i <= top; i++ ) {
  1154.     int t = tup.Entry(0).Index(i);
  1155.     if (t == 1) {
  1156.       return (0.0);
  1157.     } else if (t == 2) {
  1158.       return (1.0);
  1159.     } 
  1160.   }
  1161. }
  1162.  
  1163. // ***********************************************************************
  1164. //
  1165. // For n-dimensional space V, cross product is (V x V x ... x V) -> V,
  1166. // where the domain space repeats V (n - 1) times.
  1167. //
  1168.  
  1169. void MLM::CrossProduct(VSpace& sp)
  1170. {
  1171.   static char* sig = "void MLM::CrossProduct(VSpace&)";
  1172.  
  1173. // Build data info.  There is currently a size restriction on MultiMaps: 
  1174.  
  1175.   if (sp.Dim() > DIM_MAX) {
  1176.     errh.ErrorExit(sig, "Multimap dimension restriction exceeded", sp);
  1177.   }
  1178.   data.Numsym() = 1;
  1179.   data.IsAntisym() = TRUE;
  1180.   data.Deg(0) = sp.Dim() - 1;
  1181.   data.Dim(0) = sp.Dim();
  1182.  
  1183. // Fill in the data matrix.  Entries are either plus or minus the
  1184. // basis vectors in V.  The slot in the data matrix depends on the multi-index
  1185. // ordering:
  1186.  
  1187.   MITuple tup(data);
  1188.   int count = data.NetSize();
  1189.   data.Net() = Matrix(count, sp.MatrixSize());
  1190.  
  1191.   for (int k = 0; k < count; k++) {
  1192.     // The following line produces a g++ error, so store result in a temp:
  1193.     // data.Net()[tup.MITupletoI()]=(this->CrossResult(tup, sp.StdBasis()))[0];
  1194.     Matrix holdmat = this->CrossResult(tup, sp.StdBasis());
  1195.     data.Net()[tup.MITupletoI()] = holdmat[0]; 
  1196.     tup.NextTuple();
  1197.   }
  1198.  
  1199.   holds = MULTI_LINEAR;
  1200.   fulleval = (sp.Dim() == 1);
  1201.   if (fulleval) {
  1202.     domain = Space();
  1203.   } else if (sp.Dim() == 2) {
  1204.     domain = sp;
  1205.   } else {
  1206.   domain = VSpace("Argument space for cross product",
  1207.           SpaceList(sp, sp.Dim() - 1), FALSE);
  1208.   }
  1209.   range = sp;
  1210.   domtype = domain.NativeType();
  1211.   rettype = range.NativeType();
  1212. }
  1213.  
  1214. // ***********************************************************************
  1215. //
  1216. // Returns the image corresponding to the given tuple for the standard
  1217. // right-handed Euclidean cross product. 
  1218. //
  1219.  
  1220. Matrix MLM::CrossResult(MITuple& tup, Basis& stdbas)
  1221. {
  1222. // The tuple is only going to have one symmetry group, with one
  1223. // zero.  Find the slot with the zero, get the corresponding basis
  1224. // vector.  The sign is determined by the number of swaps needed to
  1225. // get the monotonic ordering, e.g. 12345:
  1226.  
  1227.   int top = tup.Entry(0).Size();
  1228.   Scalar swapsign;
  1229.  
  1230.   for (int i = top; i >= 0; i--) {
  1231.     int t = tup.Entry(0).Index(i);
  1232.     if (t == 0) {
  1233.       swapsign = (i % 2 == 0) ? 1.0 : -1.0; 
  1234.       // G++ 1.37.1 manages to botch this next statement up by apparently
  1235.       // destructing the temporary GeOb before copying the matrix:
  1236.       // Matrix retmat = stdbas[top - i].MatrixRep() * swapsign;
  1237.       // Fix it by assigning the GeOb to a temporary:
  1238.       GeOb holdg = stdbas[top - i];
  1239.       Matrix retmat = holdg.MatrixRep() * swapsign;
  1240.       return (retmat);
  1241.     } 
  1242.   }
  1243. }
  1244.  
  1245. // ***********************************************************************
  1246. //
  1247. // Public member functions
  1248. //
  1249. // ***********************************************************************
  1250. //
  1251. // Used to cast GeObs to MLMs.  Too large to inline under g++ 1.37.1:
  1252. //
  1253.  
  1254. MLM::MLM(GeOb& s)
  1255. {
  1256.   type = MULTI_LINEAR;
  1257.   *this = MLM(MultiMap(s));
  1258. }
  1259.  
  1260. // ***********************************************************************
  1261. //
  1262. // Used to cast a general multimap down to a multi-linear map:
  1263. //
  1264.  
  1265. MLM::MLM(MultiMap& m): (m)
  1266. {
  1267.   type = MULTI_LINEAR;
  1268.   if ((holds != MULTI_LINEAR) && (holds != NULL_MULTI)) {
  1269.       errh.ErrorExit("MLM::MLM(MultiMap&)",
  1270.           "Attempted to cast a non-multi-linear map to a multi-linear map", m);
  1271.   }
  1272. }
  1273.  
  1274. // ***********************************************************************
  1275. //
  1276. // Need to do memberwise assignment, since class members need
  1277. // to do memberwise assignment:
  1278. //
  1279.  
  1280. MLM& MLM::operator=(MLM& v)
  1281. {
  1282.   range = v.range;
  1283.   domain = v.domain;
  1284.   data = v.data;
  1285.   holds = v.holds;
  1286.   fulleval = v.fulleval;
  1287.   domtype = v.domtype;
  1288.   rettype = v.rettype;
  1289.   return *this;
  1290. }
  1291.  
  1292. // ***********************************************************************
  1293. //
  1294. // Output for debugging:
  1295. //
  1296.  
  1297. void MLM::debug_out(ostream &c, int indent) 
  1298. {
  1299.   static char* name = "MLM Object\n";
  1300.   this->data_out(c, indent, name);
  1301.   return;
  1302. }
  1303.  
  1304. // ***********************************************************************
  1305. //
  1306. // Build a multi-linear map:
  1307. //
  1308.  
  1309. MLM::MLM(VSpace& s1, IntList& symmetry, BasisList& bases,
  1310.      VSpace& s2, GeObList& vectors)
  1311. {
  1312.   static char* sig =
  1313.      "MLM::MLM(VSpace&, IntList&, BasisList&, VSpace&, GeObList&)";
  1314.   int m;
  1315.  
  1316. // Odds & ends:
  1317.  
  1318.   type = MULTI_LINEAR;
  1319.   holds = MULTI_LINEAR;
  1320.   domain = s1;
  1321.   range = s2;
  1322.   domtype = domain.NativeType();
  1323.   rettype = range.NativeType();
  1324.   fulleval = (domain.Holds() == NULL_SPACE); 
  1325.  
  1326. // Check validity of symmetry specification.  There is a symmetry
  1327. // set size restriction on multimaps: 
  1328.  
  1329.   int num = symmetry.Length();
  1330.   if (num > SYM_MAX) {
  1331.     errh.ErrorExit(sig, "Too many symmetry sets specified", symmetry);
  1332.   } else {
  1333.     data.Numsym() = num;
  1334.   }
  1335.   if (num != bases.Length()) {
  1336.     errh.ErrorExit(sig, "Incorrect number of bases specified", 
  1337.            symmetry, bases);
  1338.   }
  1339.  
  1340. // Go through the symmetry specification entry by entry:
  1341.  
  1342.   data.IsAntisym() = FALSE;
  1343.   int cpsize = 0;
  1344.   for (int i = 0; i < num; i++) {
  1345.     int val = symmetry[i];
  1346.  
  1347.     // Handle the specification of antisymmetric maps:
  1348.  
  1349.     if (val < 0) {
  1350.       if (num > 1) {
  1351.         errh.ErrorExit(sig, "Antisymmetric map can only have 1 symmetry set",
  1352.                symmetry);
  1353.       } else {
  1354.     data.IsAntisym() = TRUE;
  1355.     val = -val;
  1356.       }
  1357.     } else if (val == 0) {
  1358.       errh.ErrorExit(sig, "Symmetry list entries cannot equal 0", symmetry);
  1359.     }
  1360.  
  1361.     // Check validity within current symmetry set:
  1362.  
  1363.     if ((cpsize + val) > s1.CPSpaceSize()) {
  1364.       errh.ErrorExit(sig, 
  1365.     "Sum of symmetry list is more than number of domain space components",
  1366.     s1, symmetry);
  1367.       }
  1368.     Space curspace = s1[cpsize];
  1369.     for (int j = 1; j < val; j++) {
  1370.       if (s1[cpsize + j] != curspace) {
  1371.         errh.ErrorExit(sig,
  1372.         "Domain component spaces not identical within a symmetry set",
  1373.          s1, symmetry);
  1374.       }
  1375.     }
  1376.     if (bases[i].SpaceOf() != curspace) {
  1377.       errh.ErrorExit(sig,
  1378.         "Specified basis not in corresponding domain component space",
  1379.          s1, curspace, bases, symmetry);
  1380.     }
  1381.     cpsize += val;
  1382.     if (curspace.Dim() > DIM_MAX) {
  1383.       errh.ErrorExit(sig, "Multimap dimension restriction exceeded", curspace);
  1384.     }
  1385.     data.Deg(i) = val;
  1386.     data.Dim(i) = curspace.Dim();  
  1387.   }
  1388.  
  1389. // Sum of symmetry spec. list must equal the number of components
  1390. // in domain space:
  1391.  
  1392.   if (cpsize != s1.CPSpaceSize()) {
  1393.     errh.ErrorExit(sig, 
  1394.      "Sum of symmetry list must equal number of domain space components",
  1395.      domain, symmetry);
  1396.   }
  1397.  
  1398. // Fill in a temporary data set that will hold the representations of
  1399. // the image objects with respect to the user-specified bases.
  1400. // Need to coerce the image elements into the specified range space.
  1401. // The slot in the data matrix depends on the multi-index ordering:
  1402.  
  1403.   int vecnum = vectors.Length();
  1404.   data.Net() = Matrix(vecnum, range.MatrixSize());
  1405.   GeOb temp;
  1406.   MMData userspec = data;
  1407.   MITuple tup(data);
  1408.  
  1409.   if (vecnum != data.NetSize()) {
  1410.     errh.ErrorExit(sig, "Incorrect number of image vectors specified",
  1411.                 s1, symmetry, bases, s2, vectors);
  1412.     } 
  1413.   for (m = 0; m < vecnum; m++) {
  1414.     if (vectors[m].CanMapTo(rettype)) {
  1415.       temp = vectors[m].MapTo(rettype);
  1416.       if (temp.SpaceOf() != range) {
  1417.         errh.ErrorExit(sig, "Object cannot be mapped to range space",
  1418.                range, vectors[m]);
  1419.       } 
  1420.     } else {
  1421.       errh.ErrorExit(sig, "Object cannot be mapped to range space",
  1422.              range, vectors[m]);
  1423.     }
  1424.     userspec.Net()[tup.MITupletoI()] = temp.MatrixRep()[0];
  1425.     tup.NextTuple();
  1426.   }
  1427.  
  1428. // Now build a final data set row by row by calculating the images
  1429. // correponding to the standard basis elements:
  1430.  
  1431.   GeObList basisarg;
  1432.   tup = MITuple(data);
  1433.   for (m = 0; m < vecnum; m++) {
  1434.     MMData partial = userspec;
  1435.     this->BasisArg(tup, domain, &basisarg);
  1436.     for (int j = 0; j < basisarg.Length(); j++) {
  1437.       ScalarList coords = bases[userspec.WhichSet(j)](basisarg[j]);
  1438.       this->Eval(0, coords, &partial);
  1439.     }
  1440.     data.Net()[tup.MITupletoI()] = partial.Net()[0];
  1441.     tup.NextTuple();
  1442.   }
  1443. }
  1444.  
  1445. // ***********************************************************************
  1446. // ***********************************************************************
  1447. //
  1448. // MAM Class
  1449. //
  1450. // ***********************************************************************
  1451. // ***********************************************************************
  1452. //
  1453. // Public member functions
  1454. //
  1455. // ***********************************************************************
  1456. //
  1457. // Used to cast a general multimap down to an MAM:
  1458. //
  1459.  
  1460. MAM::MAM(MultiMap& m): (m)
  1461. {
  1462.   type = MULTI_AFFINE;
  1463.   if ((holds != MULTI_AFFINE) && (holds != NULL_MULTI)) {
  1464.     errh.ErrorExit("MAM::MAM(MultiMap&)",
  1465.           "Attempted to cast a non-multi-affine map to a multi-affine map", m);
  1466.   }
  1467. }
  1468.  
  1469. // ***********************************************************************
  1470. //
  1471. // Need to do memberwise assignment, since class members need
  1472. // to do memberwise assignment:
  1473. //
  1474.  
  1475. MAM& MAM::operator=(MAM &v)
  1476. {
  1477.   range = v.range;
  1478.   domain = v.domain;
  1479.   data = v.data;
  1480.   holds = v.holds;
  1481.   fulleval = v.fulleval;
  1482.   domtype = v.domtype;
  1483.   rettype = v.rettype;
  1484.   return *this;
  1485. }
  1486.  
  1487. // ***********************************************************************
  1488. //
  1489. // Output for debugging:
  1490. //
  1491.  
  1492. void MAM::debug_out(ostream &c, int indent) 
  1493. {
  1494.   static char* name = "MAM object\n";
  1495.   this->data_out(c, indent, name);
  1496.   return;
  1497. }
  1498.  
  1499. // ***********************************************************************
  1500. //
  1501. // Build a multi-affine map:
  1502. //
  1503.  
  1504. MAM::MAM(ASpace& s1, IntList& symmetry, BasisList& simplices,
  1505.      Space& s2,  GeObList& images)
  1506. {
  1507.   static char* sig =
  1508.      "MAM::MAM(ASpace&, IntList&, BasisList&, Space&, GeObList&)";
  1509.   int m;
  1510.  
  1511. // Odds & ends:
  1512.  
  1513.   type = MULTI_AFFINE;
  1514.   holds = MULTI_AFFINE;
  1515.   domain = s1;
  1516.   range = s2;
  1517.   if ((range.Holds() != VEC_SPACE) && (range.Holds() != AFF_SPACE)) {
  1518.     errh.ErrorExit(sig, "Range must be an affine or vector space", range);
  1519.   }
  1520.   domtype = domain.NativeType();
  1521.   rettype = range.NativeType();
  1522.   fulleval = (domain.Holds() == NULL_SPACE); 
  1523.   data.IsAntisym() = FALSE;
  1524.  
  1525. // Check validity of symmetry specification.  There is a symmetry
  1526. // set size restriction on multimaps: 
  1527.  
  1528.   int num = symmetry.Length();
  1529.   if (num > SYM_MAX) {
  1530.     errh.ErrorExit(sig, "Too many symmetry sets specified", symmetry);
  1531.   } else {
  1532.     data.Numsym() = num;
  1533.   }
  1534.   if (num != simplices.Length()) {
  1535.     errh.ErrorExit(sig, "Incorrect number of bases specified", 
  1536.            symmetry, simplices);
  1537.   }
  1538.  
  1539. // Go through the symmetry specification entry by entry:
  1540.  
  1541.   int cpsize = 0;
  1542.   for (int i = 0; i < num; i++) {
  1543.     int val = symmetry[i];
  1544.     if (val <= 0) {
  1545.       errh.ErrorExit(sig, "Symmetry list entries must be >= 1", symmetry);
  1546.     }
  1547.     
  1548.     // Check validity within current symmetry set:
  1549.  
  1550.     if ((cpsize + val) > s1.CPSpaceSize()) {
  1551.       errh.ErrorExit(sig, 
  1552.     "Sum of symmetry list is more than number of domain space components",
  1553.     s1, symmetry);
  1554.       }
  1555.     Space curspace = s1[cpsize];
  1556.     for (int j = 1; j < val; j++) {
  1557.       if (s1[cpsize + j] != curspace) {
  1558.         errh.ErrorExit(sig,
  1559.         "Domain component spaces not identical within a symmetry set",
  1560.          s1, symmetry);
  1561.       }
  1562.     }
  1563.     if (simplices[i].SpaceOf() != curspace) {
  1564.       errh.ErrorExit(sig,
  1565.         "Specified basis not in corresponding domain component space",
  1566.          s1, curspace, simplices, symmetry);
  1567.     }
  1568.     if (simplices[i].Holds() != SIMPLEX) {
  1569.       errh.ErrorExit(sig, "Specified basis not a simplex",
  1570.              simplices, simplices[i]);
  1571.     }
  1572.     cpsize += val;
  1573.     if (curspace.Dim() >= DIM_MAX) {
  1574.       errh.ErrorExit(sig, "Multimap dimension restriction exceeded", curspace);
  1575.     }
  1576.     data.Deg(i) = val;
  1577.     data.Dim(i) = curspace.Dim() + 1;  
  1578.   }
  1579.  
  1580. // Sum of symmetry spec. list must equal the number of components
  1581. // in domain space:
  1582.  
  1583.   if (cpsize != s1.CPSpaceSize()) {
  1584.     errh.ErrorExit(sig, 
  1585.      "Sum of symmetry list must equal number of domain space components",
  1586.      domain, symmetry);
  1587.   }
  1588.  
  1589. // Fill in a temporary data set that will hold the representations of
  1590. // the image objects with respect to the user-specified bases.
  1591. // Need to coerce the image elements into the specified range space.
  1592. // The slot in the data matrix depends on the multi-index ordering:
  1593.  
  1594.   int numimg = images.Length();
  1595.   data.Net() = Matrix(numimg, range.MatrixSize());
  1596.   GeOb temp;
  1597.   MMData userspec = data;
  1598.   MITuple tup(data);
  1599.  
  1600.   if (numimg != data.NetSize()) {
  1601.     errh.ErrorExit(sig, "Incorrect number of image objects specified",
  1602.                 s1, symmetry, simplices, s2, images);
  1603.     } 
  1604.   for (m = 0; m < numimg; m++) {
  1605.     if (images[m].CanMapTo(rettype)) {
  1606.       temp = images[m].MapTo(rettype);
  1607.       if (temp.SpaceOf() != range) {
  1608.         errh.ErrorExit(sig, "Object cannot be mapped to range space",
  1609.                range, images[m]);
  1610.       } 
  1611.     } else {
  1612.       errh.ErrorExit(sig, "Object cannot be mapped to range space",
  1613.              range, images[m]);
  1614.     }
  1615.     userspec.Net()[tup.MITupletoI()] = temp.MatrixRep()[0];
  1616.     tup.NextTuple();
  1617.   }
  1618.  
  1619. // Now build a final data set row by row by calculating the images
  1620. // correponding to the standard basis elements:
  1621.  
  1622.   GeObList basisarg;
  1623.   tup = MITuple(data);
  1624.   for (m = 0; m < numimg; m++) {
  1625.     MMData partial = userspec;
  1626.     this->BasisArg(tup, domain, &basisarg);
  1627.     for (int j = 0; j < basisarg.Length(); j++) {
  1628.       ScalarList coords = simplices[userspec.WhichSet(j)](basisarg[j]);
  1629.       this->Eval(0, coords, &partial);
  1630.     }
  1631.     data.Net()[tup.MITupletoI()] = partial.Net()[0];
  1632.     tup.NextTuple();
  1633.   }
  1634. }
  1635.  
  1636. // ***********************************************************************
  1637.